using BuildingBlocks.Application;
using BuildingBlocks.Application.Behaviors;
using Catalog.Api.Data;
using Microsoft.OpenApi;
using MongoDB.Driver;
using OpenIddict.Validation.AspNetCore;

namespace Catalog.Api;

internal static class DependencyInjection
{
    private static readonly string[] DefaultScopes = ["profile", "email", "roles", "offline_access"];
    private static readonly string[] allowedOrigins = ["http://localhost:5173", "https://localhost:5173"];
    
    extension(IServiceCollection services)
    {
        public IServiceCollection AddCatalogApplication()
        {
            services.AddApplication(typeof(DependencyInjection).Assembly);
            services.AddMediator(config =>
            {
                config.GenerateTypesAsInternal = true;
                config.Assemblies = [typeof(DependencyInjection).Assembly];
                config.Namespace = "Catalog.Api";
                config.PipelineBehaviors =
                [
                    typeof(LoggingPipelineBehavior<,>),
                    typeof(ValidationPipelineBehavior<,>)
                ];
                config.ServiceLifetime = ServiceLifetime.Scoped;
            });
            return services;
        }

        public IServiceCollection AddCatalogPresentation()
        {
            services.AddOpenApi();
            
            services.AddCors(options =>
            {
                options.AddPolicy("SpaCors", policy =>
                    policy.WithOrigins(allowedOrigins)
                        .AllowAnyHeader()
                        .AllowAnyMethod());
            });

            services.AddSwaggerGen(options =>
            {
                options.CustomSchemaIds(id => id.FullName!.Replace('+', '-'));

                var oauth2Scheme = new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.OAuth2,
                    Description = "OAuth2.0 Authorization Code Flow",
                    Flows = new OpenApiOAuthFlows
                    {
                        AuthorizationCode = new OpenApiOAuthFlow
                        {
                            AuthorizationUrl = new Uri("https://localhost:7051/connect/authorize"),
                            TokenUrl = new Uri("https://localhost:7051/connect/token"),
                            Scopes = new Dictionary<string, string>
                            {
                                { "profile", "Profile information" },
                                { "email", "Email address" },
                                { "roles", "User roles" },
                                { "offline_access", "Refresh token" }
                            }
                        }
                    }
                };

                options.AddSecurityDefinition("OAuth2", oauth2Scheme);
                options.AddSecurityRequirement(_ => new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecuritySchemeReference("OAuth2"),
                        DefaultScopes.ToList()
                    }
                });
            });
            services.AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
            services.AddAuthorization();
            services.AddOpenIddict()
                .AddValidation(o =>
                {
                    o.SetIssuer("https://localhost:7051/");
                    o.AddAudiences("res_server");

                    o.UseIntrospection()
                        .SetClientId("res_server")
                        .SetClientSecret("res_server_secret");

                    o.UseAspNetCore();
                    o.UseSystemNetHttp();
                });
            return services;
        }

        public IServiceCollection AddCatalogInfrastructure()
        {
            services.AddMongoContext<CatalogDbContext>(new MongoClientSettings
            {
                Servers = [new MongoServerAddress("127.0.0.1", 27017)],
                Credential = MongoCredential.CreateCredential("admin", "root", "123456")
            }, options =>
            {
                options.DatabaseName = "catalog";
                options.DefaultConventionRegistry = true;
            });
            return services;
        }
    }
}
